package org.summerframework.component.upload.ftp;

import org.summerframework.component.upload.AbstractUploadServerService;
import org.summerframework.component.upload.FileUploadServerService;
import org.summerframework.component.upload.UploadResultCode;
import org.summerframework.component.upload.exception.UploadFileException;
import org.summerframework.component.upload.ftp.pool.FtpConnectionPoolFactory;
import org.summerframework.core.exception.WebException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.function.Function;

/**
 * @author 石超
 * @version v1.0.0
 */
@Slf4j
public class FtpUploadServerServiceImpl extends AbstractUploadServerService implements FileUploadServerService {
    private FtpProperties properties;
    private FtpConnectionPoolFactory ftpConnectionPoolFactory;

    public FtpUploadServerServiceImpl(FtpProperties properties) {
        this.properties = properties;
        this.ftpConnectionPoolFactory = new FtpConnectionPoolFactory(properties);
    }

    @Override
    public String uploadFile(InputStream is, String fileName) {
        String path = getPath(fileName);
        String savePath = getPath(properties.getPath(), path);

        try {
            return ftpConnectionPoolFactory.execute(ftp -> {
                ftp.setBufferSize(1024);
                try {
                    ftp.setControlEncoding("utf-8");
                    ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
                    String filePath = FilenameUtils.getFullPath(savePath);

                    if (!ftp.changeWorkingDirectory(filePath)) {
                        ftp.makeDirectory(filePath);
                        ftp.changeWorkingDirectory(filePath);
                    }

                    ftp.setFileType(FTP.BINARY_FILE_TYPE);
                    boolean flag = ftp.storeFile(FilenameUtils.getName(path), is);

                    if (!flag) {
                        throw new UploadFileException(UploadResultCode.ERROR_UPLOAD_FAILURE);
                    }
                    return path;
                } catch (WebException e) {
                    throw e;
                } catch (Exception e) {
                    throw new UploadFileException(UploadResultCode.ERROR_UPLOAD_FAILURE, e);
                }
            });
        } catch (WebException e) {
            throw e;
        } catch (Exception e) {
            throw new UploadFileException(UploadResultCode.ERROR_UPLOAD_FAILURE, e);
        }
    }

    @Override
    public byte[] getFileBytes(String path) {
        try {
            return ftpConnectionPoolFactory.execute(ftp -> {

                try (InputStream input = ftp.retrieveFileStream(getPath(properties.getPath(), path))) {
                    return IOUtils.toByteArray(input);
                } catch (IOException e) {
                    return EMPTY_BYTES;
                } finally {
                    try {
                        ftp.completePendingCommand();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (Exception e) {
            return EMPTY_BYTES;
        }
    }

    @Override
    public InputStream getInputStream(String path) throws Exception {
        return ftpConnectionPoolFactory.execute(ftp -> {
            try {
                return ftp.retrieveFileStream(getPath(properties.getPath(), path));
            } catch (IOException e) {
                return new ByteArrayInputStream(EMPTY_BYTES);
            }
        });
    }

    @Override
    public void deleteFile(String path) {
        try {
            ftpConnectionPoolFactory.execute(ftp -> {
                try {
                    ftp.deleteFile(getPath(properties.getPath(), path));
                } catch (IOException e) {
                    log.warn("删除失败", e);
                }
                return 0;
            });
        } catch (Exception e) {
            log.warn("删除失败", e);
        }
    }

    private <T> T execute(Function<FTPClient, T> function) {
        FTPClient ftp = null;
        try {
            ftp = new FTPClient();
            // 连接FPT服务器,设置IP及端口
            ftp.connect(properties.getService(), properties.getPort());
            // 设置用户名和密码
            ftp.login(properties.getAccount(), properties.getPassword());
            // 设置连接超时时间,5000毫秒
            ftp.setConnectTimeout((int) properties.getConnectTimeout().toMillis());
            // 设置中文编码集，防止中文乱码
            ftp.setControlEncoding("UTF-8");
            if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
                ftp.disconnect();
                throw new WebException(UploadResultCode.ERROR_INIT_CLIENT, "未连接到FTP，用户名或密码错误");
            }
            return function.apply(ftp);
        } catch (WebException e) {
            throw e;
        } catch (Exception e) {
            throw new WebException(UploadResultCode.ERROR_INIT_CLIENT, e);
        } finally {
            if (ftp != null) {
                try {
                    ftp.disconnect();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
